package com.coincalf.wallet.task.syncblock;

import com.alibaba.fastjson.JSONObject;
import com.coincalf.dto.CoinInfoDto;
import com.coincalf.feign.CoinServiceClient;
import com.coincalf.framework.enums.RechargeStatus;
import com.coincalf.wallet.blockchain.TrxService;
import com.coincalf.wallet.blockchain.bean.trx.*;
import com.coincalf.wallet.entity.CoinRecharge;
import com.coincalf.wallet.entity.SyncBlockHeight;
import com.coincalf.wallet.entity.UserAddress;
import com.coincalf.wallet.service.CoinRechargeService;
import com.coincalf.wallet.service.SyncBlockHeightService;
import com.coincalf.wallet.service.UserAddressService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

/**
 * TRX区块扫描任务 写入用户充币纪录
 */
@Component
@EnableScheduling
@Transactional(rollbackFor = Exception.class)
public class SyncTrxNewBlock {

    private static final Logger log = LoggerFactory.getLogger(SyncTrxNewBlock.class);

    @Autowired
    private UserAddressService userAddressService;
    @Autowired
    private CoinRechargeService rechargeService;
    @Autowired
    private SyncBlockHeightService syncBlockHeightService;
    @Autowired
    private CoinServiceClient coinServiceClient;
    @Autowired
    private TrxService trxService;


    //波场区块扫描 每10秒扫描18个区块
    //@Scheduled(cron = "0/10 * * * * ?")
    public void syncTask() {
        log.warn("即将执行TRX区块扫描任务...");
        long startTime = System.currentTimeMillis();
        //从数据库获取已经同步的区块高度
        SyncBlockHeight trxSyncedBlockHeight = syncBlockHeightService.selectOneByCoinName("TRX");
        if (trxSyncedBlockHeight == null || trxSyncedBlockHeight.getSyncedBlockHeight() == null) {
            log.warn("无法找到TRX同步缓存高度");
            return;
        } else if (trxSyncedBlockHeight.getStatus() == 0) {
            log.info("本次TRX同步状态已关闭");
            return;
        }
        long height = trxSyncedBlockHeight.getSyncedBlockHeight();
        //调用RPC接口查询区块信息
        TrxBlocks blocks;
        long newestBlock;
        try {
            newestBlock = trxService.getNowBlockHeight();
            if (height >= newestBlock) {
                log.info("已同步TRX高度追上最新高度啦 height={} newestBlock={}", height, newestBlock);
                return;
            }
            blocks = TrxService.getBlocks(height, height + 18);
        } catch (Exception e) {
            log.error("TRX RPC调用失败 e={}", e);
            return;
        }

        //本次同步后需要增加的区块高度
        long addHeight = newestBlock - height > 18 ? 18 : newestBlock - height;

        //遍历区块所有交易详情
        List<TrxBlock> trxBlocks = blocks.getBlocks();
        if (CollectionUtils.isEmpty(trxBlocks)) {
            log.info("区块信息为空");
            return;
        }

        //获取所有用户TRX地址
        HashMap<String, Long> userAddress = new HashMap<>(2048);
        //从数据库获取TRC20代币合约地址
        List<CoinInfoDto> contractAddresses = coinServiceClient.selectCoinInfoByMainCoinId(trxSyncedBlockHeight.getCoinId());
        for (CoinInfoDto contractAddress : contractAddresses) {
            String address = contractAddress.getCoinContractAddress();
            if (StringUtils.isNotBlank(address)) {
                userAddress.put(address, -1L);
            } else {
                log.warn("该币种未配置合约地址 coinId={} coinName={}", contractAddress.getCoinId(), contractAddress.getCoinName());
            }
        }

        //用户TRX地址列表
        List<UserAddress> userAddresses = userAddressService.selectListByCoinId(trxSyncedBlockHeight.getCoinId());
        for (UserAddress address : userAddresses) {
            String chainAddress = address.getAddress();
            userAddress.put(chainAddress, address.getUserId());
        }

        for (TrxBlock block : trxBlocks) {
            List<TrxTransaction> txs = block.getTransactions();
            if (CollectionUtils.isEmpty(txs)) {
                log.info("该区块没有交易...");
                continue;
            }
            for (TrxTransaction tx : txs) {
                List<TrxTransaction.Ret> ret = tx.getRet();
                if (CollectionUtils.isEmpty(ret)) {
                    continue;
                }
                String contractRet = ret.get(0).getContractRet();
                if (!"SUCCESS".equals(contractRet)) {
                    continue;
                }
                //交易时间 从区块获取 从交易获取可能为null
                TrxRawData rwaDate = tx.getRwaDate();
                Long timestamp = block.getBlockHeader().getRawData().getTimestamp();
                Date date = new Date(timestamp);
                //查询交易
                List<TrxContract> contracts = rwaDate.getContracts();
                if (CollectionUtils.isEmpty(contracts)) {
                    log.info("contract为空");
                    continue;
                }
                TrxContract trxContract = contracts.get(0);
                String type = trxContract.getType();

                //构建充币记录表
                CoinRecharge coinRecharge = new CoinRecharge();
                BigDecimal amount = BigDecimal.ZERO;
                TrxParameter parameter = trxContract.getParameter();
                if (null == parameter) {
                    continue;
                }
                Object value = parameter.getValue();
                JSONObject valueJSON = JSONObject.parseObject(JSONObject.toJSONString(value));
                String fromAddress = trxService.hexString2Base58check(valueJSON.getString("owner_address"));
                String toAddress = null;
                if ("TransferContract".equals(type)) {
                    //TRX转账
                    String toAddressHex = valueJSON.getString("to_address");
                    toAddress = trxService.hexString2Base58check(toAddressHex);
                    if (!userAddress.containsKey(toAddress)) {
                        continue;
                    }
                    coinRecharge.setCoinId(trxSyncedBlockHeight.getCoinId());
                    coinRecharge.setCoinName(trxSyncedBlockHeight.getCoinName());
                    //包含交易
                    log.info("TRX地址有查询到TRX交易 address={} txHash={}", toAddress, tx.getTxId());
                    amount = valueJSON.getBigDecimal("amount").divide(TrxService.TRX2SUN, 6, RoundingMode.HALF_DOWN);
                } else if ("TriggerSmartContract".equals(type)) {
                    //TRC20代币交易处理
                    //合约地址 16进制格式
                    String contractAddressHex = valueJSON.getString("contract_address");
                    String contractBase58 = trxService.hexString2Base58check(contractAddressHex);
                    //data数据解析,仅查询标准TRC20代币合约转账方法
                    String data = valueJSON.getString("data");
                    if (StringUtils.isNotBlank(data) && data.length() == 136) {
                        String contractMethod = data.substring(0, 8);
                        if (TrxService.TRC20_TRANSFER_HEX.equals(contractMethod)) {
                            String toAddressHex = data.substring(30, 72);
                            if (toAddressHex.startsWith("00")) {
                                toAddressHex = "41" + toAddressHex.substring(2);
                                toAddress = trxService.hexString2Base58check(toAddressHex);
                                String amountHex = data.substring(72);
                                BigInteger bigInteger = new BigInteger(amountHex, 16);
                                //转账金额
                                amount = BigDecimal.valueOf(bigInteger.longValue()).divide(TrxService.TRX2SUN, 6, RoundingMode.DOWN);
                                //TRC20代币交易处理
                                for (CoinInfoDto contractAddress : contractAddresses) {
                                    if (StringUtils.isNotBlank(contractAddress.getCoinContractAddress())        //合约地址非空判断
                                            && contractAddress.getCoinContractAddress().equals(contractBase58)  //具体TRC20代币
                                            && !contractAddress.getCoinContractAddress().equals(toAddress)) {   //收款地址合法性校验
                                        //只记录系统内的用户地址交易
                                        if (userAddress.containsKey(toAddress)) {
                                            log.info("TRX地址有查询到{}-TRC20交易 address={} txHash={}", contractAddress.getCoinName(), toAddress, tx.getTxId());
                                            coinRecharge.setCoinId(contractAddress.getCoinId());
                                            coinRecharge.setCoinName(contractAddress.getCoinName());
                                            coinRecharge.setContractAddress(contractAddress.getCoinContractAddress());
                                        }
                                    }
                                }
                            }
                        }
                    }
                } /*else {
                    continue;
                    //TODO 其他 冻结解冻等等 待定...
                }*/
                if (StringUtils.isNotBlank(toAddress)) {
                    coinRecharge.setFromAddress(fromAddress);
                    coinRecharge.setAddress(toAddress);
                    coinRecharge.setMum(amount);
                    coinRecharge.setTxid(tx.getTxId());
                    coinRecharge.setStatus(RechargeStatus.PENDING.getCode());
                    coinRecharge.setBlockNumber(Math.toIntExact(block.getBlockHeader().getRawData().getNumber()));
                    //TODO 交易消耗资源情况...
                    coinRecharge.setFee(null);
                    coinRecharge.setCollected(0);
                    coinRecharge.setCreated(date);//交易上链时间(打包时间)
                    coinRecharge.setLastUpdateTime(new Date());
                    //将TRX交易写入数据库
                    Long userId = userAddress.get(toAddress);
                    coinRecharge.setUserId(userId);
                    //查询交易是否已经存在 基本不可能
                    CoinRecharge exits = rechargeService.selectOneByCoinIdAndTxIdAndToAddress(
                            coinRecharge.getCoinId(),
                            coinRecharge.getTxid(),
                            coinRecharge.getAddress()
                    );
                    if (exits == null) {
                        rechargeService.insert(coinRecharge);
                        log.info("成功写入TRX or TRC20交易到数据库 chainOrderModel={}", coinRecharge.toString());
                    } else {
                        log.warn("该{}交易已经存在，请勿重复写入 txId={}", coinRecharge.getCoinName(), coinRecharge.getTxid());
                    }
                }
            }
        }

        height = height + addHeight;
        trxSyncedBlockHeight.setSyncedBlockHeight(Math.toIntExact(height));
        trxSyncedBlockHeight.setLastUpdateTime(new Date());
        syncBlockHeightService.insert(trxSyncedBlockHeight);

        try {
            Thread.sleep(150);
        } catch (
                InterruptedException e) {
            e.printStackTrace();
        }

        long endTime = System.currentTimeMillis();
        long spent = (endTime - startTime) / 1000;
        log.info("本次TRX区块同步任务结束，耗时={}秒 height={}", spent, height);
    }

    public static void main(String[] args) {
        String data = "a9059cbb000000000000000000000041c165ca7aecbe50ecab85d136d0c3c27b4537080c0000000000000000000000000000000000000000000000000000000000989680";
        String toAddressHex = data.substring(30, 72);
        if (toAddressHex.startsWith("00")) {
            toAddressHex = "41" + toAddressHex.substring(2);
        }
        String s = new TrxService().hexString2Base58check(toAddressHex);
        System.out.println(s);
    }

}
